#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unistd.h>
#include <string>
#include <pthread.h>
#include <cstring>
#include "protocal.hpp"
#include "log.hpp"
#define NUM 1024
using namespace std;
typedef function<bool(const Request& ,Response&)> func_t; // 回调函数，用来处理业务逻辑

static const uint16_t gport = 8080;
static const int gbacklog = 5; // 这里暂时不需要了解
enum
{
    USAGE_ERROR = 1,
    SOCK_ERROR,
    BIND_ERROR,
    LISTEN_ERROR
};

void handlerEntery(int sock, func_t func)
{
    string inbuffer; // 用来存放接受的数据
    while (true)
    {
        // 1.收到客户端发来的信息
        string req_text, req_str; // req_text是存放接收到的全部报文，req_str是接收到的文本信息
        if (!recvPackage(sock, inbuffer, &req_text))
            return;
        // 2.去掉报文，然后反序列化，得到结构化信息
        //我们这里输入采用引用，输出使用指针
        if(!deLength(req_text,&req_str))
            return;
        //反序列化，变成一个结构化数据
        Request req;
        if(!req.deserialize(req_str))
            return;
        // 3.处理业务逻辑，回调上层函数
        Response resp;
        func(req,resp);
        // 4.将得到的结果先序列化，然后再加上报文
        string resp_str,resp_text;
        resp.serialize(&resp_str);
        //加报文
        enLength(resp_str,&resp_text);
        // 5.发送数据给客服端
        send(sock,resp_text.c_str(),resp_text.size(),0);
    }
}

class calserver
{
public:
    calserver(const uint16_t &port = gport)
        : _port(port), _listenfd(-1)
    {
    }
    void initServer()
    {
        // 1.创建tcp套接字
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            // 错误信息放到日志中
            logmessage(FATAL, "socket create fail");
            exit(SOCK_ERROR);
        }
        logmessage(NORMAL, "socket create success");
        // 绑定bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        if (bind(_listenfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            // 错误信息放到日志中
            logmessage(FATAL, "bind fail");
            exit(BIND_ERROR);
        }
        logmessage(NORMAL, "bind success");
        // 监听
        if (listen(_listenfd, gbacklog) != 0)
        {
            // 错误信息放到日志中
            logmessage(FATAL, "listen fail");
            exit(LISTEN_ERROR);
        }
        logmessage(NORMAL, "listen success");
    }
    void start(func_t func)
    {
        signal(SIGCHLD,SIG_IGN); // 让父进程不需要等待子进程。
        for (;;)
        {
            // 连接,注意这里连接之后是创建新的文件描述符，和listenfd是不相同的！
            struct sockaddr_in client; // 这里要连接的client
            socklen_t len = sizeof(client);
            int sock = accept(_listenfd, (struct sockaddr *)&client, &len);
            // 这里的创建出新的文件描述符，可以直接用于通信，这里本质就是文件操作
            if (sock < 0)
            {
                // 错误信息放到日志中
                logmessage(ERROR, "accept fail");
                continue; // 这里创建失败可以再次创建，不是严重的错误
            }
            logmessage(NORMAL, "accept succes");
            cout << "create sock : " << sock << endl;
            // version2 多线程
            pid_t id = fork();
            if (id == 0)
            {
                // 子进程
                // 关闭父进程的listenfd，因为子进程不需要完成监听工作
                close(_listenfd);
                // if(fork()>0) exit(0); // 这里让子进程退出，让孙子进程完成任务，完成任务后会被OS领养
                // 序列化，反序列化，以及报头的增减
                handlerEntery(sock, func);
                close(sock);
                exit(0);
            }
            // 每次使用完都关闭，否者会导致文件描述符越用越少。
            close(sock); // 这里父进程可以关闭，因为这里的关闭并不是真正的关闭而是引用计数--。
            // 等待子进程，这里可以通过设置信号解决，但是也可以通过孤儿进程被OS领养来解决
        }
    }

    ~calserver()
    {
    }

private:
    uint16_t _port; // 端口号
    int _listenfd;  // 监听套接字
};